import { any } from "../../../core/logic";
import { color } from "../../../core/color";
import { Engine } from "../../../Engine";
import { Pass } from "../Pass";
import { Effect, EffectType } from "./Effect";
import { bloom_vert } from "./shaders/bloom_vert";
import { blur_frag, merge_frag, blend_frag } from "./shaders/bloomSpiral_frag";
import { Register } from "../../../register/Register";
import { ComponentType } from "../../../component/Component";
/**
 * 螺旋发光
 * @class
 * @memberof w.effects
 */
class BloomSpiral extends Effect {
  /**
   * @constructor
   * @param {*} opts
   * @param {Number} opts.blurColor 缺省值#cccccc,模糊颜色。
   * @param {Number} opts.blurIntensity 缺省值0.0,模糊强度。
   */
  constructor(opts = {}) {
    super(opts);
    this.type = EffectType.Blur;
    this.sampler = Engine.instance.device.createSampler({
      magFilter: "linear", //nearest linear
      minFilter: "linear",
    });
    this.init()
    this.density = any(opts.density, 0.001225);
    this.maxRadius = any(opts.maxRadius, 0.01);
    this.strength = any(opts.strength, 1);
  }
  get density() {
    return this._density;
  }
  set density(v) {
    this._density = v;
    Engine.instance.queue.writeBuffer(
      this.blurPipeLine.densityBuffer,
      0,
      new Float32Array([v])
    );
    this.update();
  }
  get strength() {
    return this._strength;
  }
  set strength(v) {
    this._strength = v;
    Engine.instance.queue.writeBuffer(
      this.mergePipeline.strengthBuffer,
      0,
      new Float32Array([v])
    );
    this.update();
  }
  get maxRadius() {
    return this._maxRadius;
  }
  set maxRadius(v) {
    this._maxRadius = v;
    Engine.instance.queue.writeBuffer(
      this.blurPipeLine.maxRadiusBuffer,
      0,
      new Float32Array([v])
    );
    this.update();
    return this;
  }
  init() {
    let resx = Math.round(Engine.instance.size[0] / 2);
    let resy = Math.round(Engine.instance.size[1] / 2);
    // 模糊
    this.blurTexture = Engine.instance.device.createTexture({
      size: [resx, resy],
      format: Engine.instance.format,
      sampleCount: 1,
      usage: GPUTextureUsage.RENDER_ATTACHMENT | GPUTextureUsage.TEXTURE_BINDING
    });
    this.blurTexture.id = `blurTexture`
    this.blurTexture.view = this.blurTexture.createView({ label: "blurTexture" })
    this.blurDescript = {
      colorAttachments: [
        {
          view: this.blurTexture.view,
          loadOp: "clear",
          clearValue: { r: 0, g: 0, b: 0, a: 0 },
          storeOp: "store",
        },
      ],
    };
    // 创建模糊材质
    this.createBlurMaterial()
    // 创建混和材质
    this.blendTexture = Engine.instance.device.createTexture({
      size: [Engine.instance.size[0], Engine.instance.size[1]],
      format: Engine.instance.format,
      sampleCount: 1,
      usage: GPUTextureUsage.RENDER_ATTACHMENT | GPUTextureUsage.TEXTURE_BINDING
    });
    this.blendTexture.id = `UnrealBloomPass.blend`
    this.blendTexture.view = this.blendTexture.createView({ label: "blend texture" })
    const depthTexture = Engine.instance.device.createTexture({
      label: "depth",
      size: Engine.instance.size,
      format: "depth24plus-stencil8",
      mipLevelCount: 1,
      sampleCount: 1,
      usage: GPUTextureUsage.RENDER_ATTACHMENT | GPUTextureUsage.TEXTURE_BINDING | GPUTextureUsage.COPY_SRC,
    });
    depthTexture.view = depthTexture.createView()
    this.blendDescript = {
      colorAttachments: [
        {
          view: this.blendTexture.view,
          loadOp: "clear",
          clearValue: { r: 0, g: 0, b: 0, a: 0 },
          storeOp: "store",
        },
      ],
      depthStencilAttachment: {
        view: depthTexture.view,
        depthClearValue: 1.0,
        depthLoadOp: "clear",
        depthStoreOp: "store",
        stencilClearValue: 0,
        stencilLoadOp: "clear",
        stencilStoreOp: "store",
      } // 复用主渲染的深度模板附件
    };
    this.createBlendMateria()
    // 合并
    this.createMergePipeline()
  }

  createBlurMaterial() {
    if (!this.blurPipeLine) {
      this.blurPipeLine = Engine.instance.device.createRenderPipeline({
        label: "blur Pipeline",
        layout: `auto`,
        vertex: {
          module: Engine.instance.device.createShaderModule({
            code: bloom_vert,
          }),
          entryPoint: "main",
        },
        fragment: {
          module: Engine.instance.device.createShaderModule({
            code: blur_frag,
          }),
          entryPoint: "main",
          targets: [
            {
              format: Engine.instance.format,
            },
          ],
        },
        primitive: {
          topology: "triangle-list",
          cullMode: "back",
          frontFace: "ccw",
        },
      });
      // 创建新的uniform buffers
      this.densityBuffer = Engine.instance.device.createBuffer({
        label: "density",
        size: 4,
        usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST,
      });
      this.maxRadiusBuffer = Engine.instance.device.createBuffer({
        label: "maxRadius",
        size: 4,
        usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST,
      });

      this.blurPipeLine.densityBuffer = this.densityBuffer;
      this.blurPipeLine.maxRadiusBuffer = this.maxRadiusBuffer;
    }

  }

  darkenNonBloomed() {
    const meshs = Register.instance.manager(ComponentType.Mesh).components;
    meshs.forEach((mesh, id) => {
      if (!mesh?.shine) {
        const enableDarkerBuffer = mesh.material.getParam("enableDarker");
        if (enableDarkerBuffer) {
          enableDarkerBuffer.buffer(new Float32Array([1]));
        }
      }
    });
  }
  restoreMaterial() {
    const meshs = Register.instance.manager(ComponentType.Mesh).components;
    meshs.forEach((mesh, id) => {
      const enableDarkerBuffer = mesh.material.getParam("enableDarker");
      if (enableDarkerBuffer) {
        if (!mesh?.shine) {
          // 不发光的物体恢复正常
          enableDarkerBuffer.buffer(new Float32Array([0]));
        }
      }
    });
  }
  createBlurBind(texture) {
    if (this.blurBind && this.blurBind.id != texture.id) {
      this.blurBind = null;
    }
    if (!this.blurBind) {
      this.blurBind = Engine.instance.device.createBindGroup({
        label: "blur group",
        layout: this.blurPipeLine.getBindGroupLayout(0),
        entries: [
          {
            binding: 0,
            resource: texture.view,
          },
          {
            binding: 1,
            resource: this.sampler,
          },
          {
            binding: 2,
            resource: {
              buffer: this.densityBuffer,
              size: 4,
            },
          },
          {
            binding: 3,
            resource: {
              buffer: this.maxRadiusBuffer,
              size: 4,
            },
          },
        ],
      });
      this.blurBind.id = texture.id;
    }
    return this.blurBind;
  }
  createBlendMateria() {
    if (!this.blendPipeline) {
      this.blendPipeline = Engine.instance.device.createRenderPipeline({
        label: "blendPipeline",
        layout: `auto`,
        vertex: {
          module: Engine.instance.device.createShaderModule({
            code: bloom_vert,
          }),
          entryPoint: "main",
        },
        fragment: {
          module: Engine.instance.device.createShaderModule({
            code: blend_frag,
          }),
          entryPoint: "main",
          targets: [
            {
              format: Engine.instance.format,
              blend: {
                color: {
                  operation: 'add',
                  srcFactor: 'src-alpha',
                  dstFactor: 'one'
                },
                alpha: {
                  operation: 'add',
                  srcFactor: 'one',
                  dstFactor: 'one'
                }
              },
            },
          ],
        },
        depthStencil: {
          format: 'depth24plus-stencil8',
          depthWriteEnabled: false,
          depthCompare: 'always',
        },
        primitive: {
          topology: "triangle-list",
          cullMode: "back",
          frontFace: "ccw",
        },
      });
      this.blendPipeline.opacityBuffer = Engine.instance.device.createBuffer({
        label: "blendPipeline",
        size: 4, // f32 = 4 bytes
        usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST,
      });
      Engine.instance.queue.writeBuffer(
        this.blendPipeline.opacityBuffer,
        0,
        new Float32Array([1.0])
      );
    }
  }
  createBlendBindGroup(texture) {
    // 如果已存在 bindGroup 且纹理 ID 相同，直接返回缓存的 bindGroup
    if (this.blendBindGroup && this.blendBindGroup.id === texture.id) {
      return this.blendBindGroup;
    }
    // 创建新的 bindGroup
    this.blendBindGroup = Engine.instance.device.createBindGroup({
      label: "blend group",
      layout: this.blendPipeline.getBindGroupLayout(0),
      entries: [
        {
          binding: 0,
          resource: texture.view,
        },
        {
          binding: 1,
          resource: this.sampler,
        },
        {
          binding: 2,
          resource: {
            buffer: this.blendPipeline.opacityBuffer,
          },
        },
      ],
    });

    // 缓存纹理 ID
    this.blendBindGroup.id = texture.id;

    return this.blendBindGroup;
  }
  createMergePipeline() {
    if (!this.mergePipeline) {
      this.mergePipeline = Engine.instance.device.createRenderPipeline({
        label: "Merge Pipeline",
        layout: "auto",
        vertex: {
          module: Engine.instance.device.createShaderModule({
            code: bloom_vert,
          }),
          entryPoint: "main",
        },
        fragment: {
          module: Engine.instance.device.createShaderModule({
            code: merge_frag,  // 需要创建merge_frag着色器
          }),
          entryPoint: "main",
          targets: [{
            format: Engine.instance.format,
          }],
        },
        primitive: {
          topology: "triangle-list",
          cullMode: "back",
          frontFace: "ccw",
        },
      });
      this.mergePipeline.strengthBuffer = Engine.instance.device.createBuffer({
        label: "mergePipeline",
        size: 4, // f32 = 4 bytes
        usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST,
      });
      Engine.instance.queue.writeBuffer(
        this.mergePipeline.strengthBuffer,
        0,
        new Float32Array([this.strength])
      );
    }
  }
  createMergeBindGroup(originalTexture) {
    if (this.mergeBindGroup && this.mergeBindGroup.id != originalTexture.id) {
      this.mergeBindGroup = null;
    }
    if (!this.mergeBindGroup) {
      this.mergeBindGroup = Engine.instance.device.createBindGroup({
        label: "merge group",
        layout: this.mergePipeline.getBindGroupLayout(0),
        entries: [
          {
            binding: 0,
            resource: originalTexture.view,
          },
          {
            binding: 1,
            resource: this.blendTexture.view,
            // resource: this.blurTexture.view,
          },
          {
            binding: 2,
            resource: this.sampler,
          },
          {
            binding: 3,
            resource: {
              buffer: this.mergePipeline.strengthBuffer,
              size: 4,
            },
          }
        ],
      });
      this.mergeBindGroup.id = originalTexture.id;
    }
    return this.mergeBindGroup;
  }
  onRender(commandEncoder) {
    // 1. 设置材质状态（发光物体正常显示，非发光物体变黑）
    this.darkenNonBloomed();
    // 2. 渲染到 shine 纹理（此时应该只有发光物体有颜色）
    let commandEncoderShine = Engine.instance.device.createCommandEncoder();
    const ShineDescript = this.getShineDescript();
    const meshRednerCom = Register.instance.manager(ComponentType.MeshRender).get("x");
    const bundles = meshRednerCom.bundles;
    const renderPassShine = commandEncoderShine.beginRenderPass(ShineDescript);
    renderPassShine.executeBundles(bundles);
    renderPassShine.end();
    if (!this.shineTexture) return;
    // 3. 模糊处理
    const renderPass = commandEncoderShine.beginRenderPass(this.blurDescript);
    renderPass.setPipeline(this.blurPipeLine);
    let bindGroup = this.createBlurBind(this.shineTexture)
    renderPass.setBindGroup(0, bindGroup);
    renderPass.draw(3);
    renderPass.end();
    // 混和
    const renderPassBlend = commandEncoderShine.beginRenderPass(this.blendDescript);
    renderPassBlend.setPipeline(this.blendPipeline);
    let blendBindGroup = this.createBlendBindGroup(this.blurTexture);
    renderPassBlend.setBindGroup(0, blendBindGroup);
    renderPassBlend.draw(3);
    renderPassBlend.end();
    Engine.instance.device.queue.submit([commandEncoderShine.finish()]);

    // 5. 恢复所有物体的正常显示
    this.restoreMaterial();
    // 6. 合并原始场景和发光效果
    const pass = super.onRender(commandEncoder);
    const renderPassMerge = commandEncoder.beginRenderPass(pass);
    renderPassMerge.setPipeline(this.mergePipeline);
    const texture = Pass.instance().readAttach;
    const mergeBindGroup = this.createMergeBindGroup(texture);
    renderPassMerge.setBindGroup(0, mergeBindGroup);
    renderPassMerge.draw(3);
    renderPassMerge.end();
  }

  getShineDescript() {
    if (!this.shineTexture ||
      this.shineTexture.width !== Engine.instance.size[0] ||
      this.shineTexture.height !== Engine.instance.size[1]) {
      // 创建发光颜色附件
      this.shineTexture = Engine.instance.device.createTexture({
        size: [Engine.instance.size[0], Engine.instance.size[1]],
        format: Engine.instance.format,
        sampleCount: 1,
        usage: GPUTextureUsage.RENDER_ATTACHMENT | GPUTextureUsage.TEXTURE_BINDING
      });
      this.shineTexture.view = this.shineTexture.createView({ label: "shine texture" })
      this.shineTexture.id = `shineTexture_${Engine.instance.size[0]}_${Engine.instance.size[1]}`
      this.shineDescript = {
        colorAttachments: [
          {
            view: Pass.instance().colorTexture.view,
            resolveTarget: this.shineTexture.view,
            loadOp: "clear",
            clearValue: { r: 0, g: 0, b: 0, a: 0 },
            storeOp: "store",
          },
        ],
        depthStencilAttachment: {
          view: Pass.instance().depthAttach.view,
          depthClearValue: 1.0,
          depthLoadOp: "clear",
          depthStoreOp: "store",
          stencilClearValue: 0,
          stencilLoadOp: "clear",
          stencilStoreOp: "store",
        } // 复用主渲染的深度模板附件
      };
      // 如果开启了GBuffer,添加position和normal附件
      if (Engine.instance.enableGbuffer) {
        this.shineDescript.colorAttachments.push({
          label: "position",
          view: Pass.instance().postionAttach.view,
          loadOp: "clear",
          clearValue: { r: 1.0, g: 1.0, b: 1.0, a: 0 },
          storeOp: "store"
        });
        this.shineDescript.colorAttachments.push({
          label: "normal",
          view: Pass.instance().normalAttach.view,
          loadOp: "clear",
          clearValue: { r: 1.0, g: 1.0, b: 1.0, a: 0 },
          storeOp: "store"
        });
      }
      // 如果开启了拾取功能,添加pick附件
      if (Engine.instance.enablePick) {
        this.shineDescript.colorAttachments.push({
          label: "pick",
          view: Pass.instance().pickAttach.view,
          loadOp: "clear",
          clearValue: { r: 1.0, g: 1.0, b: 1.0, a: 0 },
          storeOp: "store"
        });
      }
    }
    return this.shineDescript;
  }
  getDescript() {
    if (this._descript) {
      if (
        this._descript.size[0] !== Engine.instance.size[0] ||
        this._descript.size[1] !== Engine.instance.size[1]
      ) {
        this._descript = null;
      } else {
        this._descript.colorAttachments[0].view =
          Pass.instance().writeAttach.view;
      }
    }
    if (!this._descript) {
      this._descript = {
        colorAttachments: [
          {
            view: Pass.instance().writeAttach.view,
            loadOp: "clear",
            clearValue: { r: 1.0, g: 1.0, b: 1.0, a: 1.0 },
            storeOp: "store",
          },
        ],
      };
      this._descript.size = Engine.instance.size;
    }
    return this._descript;
  }
}
export { BloomSpiral };
